/**
 * Class: RandomUtils
 *
 * Purpose: Handy utilities for bringing randomness under control
 *
 * @author Ken Scott
 */
package org.openfantasia.util;

import org.openfantasia.util.misc.Weighted;

import java.util.*;

public class RandomUtils {

	private static Random RANDOM = new Random((System.currentTimeMillis() + (long) Utils.class.getName().hashCode()) % 5501L);


	public static void setRandom(Random random) {
		RANDOM = random;
	}


	public static Random getRandom() {
		return RANDOM;
	}


	public static void waitRandom(long max) {
		DateTime.waitMilliseconds(Math.max(0, RandomUtils.randomLong(max)));
	}


	public static void waitRandom(long min, long max) {
		DateTime.waitMilliseconds(Math.max(0, RandomUtils.randomLong(min, max)));
	}


	public static double randomFloat() {
		synchronized (RANDOM) {
			return RANDOM.nextFloat();
		}
	}


	public static double randomDouble() {
		synchronized (RANDOM) {
			return RANDOM.nextDouble();
		}
	}


	public static boolean randomProbability() {
		return 0.5d > randomDouble();
	}


	public static boolean randomProbability(double probability) {
		return probability > randomDouble();
	}


	public static double randomDouble(double range) {
		return randomDouble() * range;
	}


	public static double randomDouble(double min, double max) {
		return min + (randomDouble(1.0) * (max - min));
	}


	public static int randomInt(int range) {
		synchronized (RANDOM) {
			return RANDOM.nextInt(range);
		}
	}


	public static int randomInt(int min, int max) {
		return min + randomInt(max - min + 1);
	}


	public static long randomLong(long range) {
		synchronized (RANDOM) {
			return Math.abs(RANDOM.nextLong()) % range;
		}
	}


	public static long randomLong(long min, long max) {
		return min + randomLong(max - min + 1);
	}


	public static String randomString(int minLength, int maxLength) {
		return randomString(Constants.UPPER_LOWER_DIGITS, minLength, maxLength);
	}


	public static String randomText(int minLength, int maxLength) {
		return randomString(Constants.UPPER_LOWER, minLength, maxLength);
	}


	public static String randomUpper(int minLength, int maxLength) {
		return randomString(Constants.UPPERCASE_ALPHABET, minLength, maxLength);
	}


	public static String randomLower(int minLength, int maxLength) {
		return randomString(Constants.LOWERCASE_ALPHABET, minLength, maxLength);
	}


	public static double randomGaussian() {
		synchronized (RANDOM) {
			return RANDOM.nextGaussian();
		}
	}


	public static boolean randomBoolean() {
		return randomDouble() > 0.5;
	}


	public static double randomSign() {
		return randomBoolean() ? -1 : 1;
	}


	public static char randomChar(String src) {
		if (!TextUtils.hasText(src)) {
			return 0;
		}
		return src.charAt(randomInt(src.length()));
	}


	public static char randomChar(char[] src) {
		if ((src == null) || (src.length == 0)) {
			return 0;
		}
		return src[randomInt(src.length)];
	}


	public static char randomChar(List<Character> src) {
		if (src.isEmpty()) {
			return 0;
		}
		return src.get(randomInt(src.size()));
	}


	public static String randomString(String charSource, int minLength, int maxLength) {
		String result = "";
		int srcLength = charSource.length();
		int resultLength = randomInt(minLength, maxLength);
		for (int i = 0; i < resultLength; i++) {
			result += charSource.charAt(randomInt(srcLength));
		}
		return result;
	}


	public static String randomString(String... src) {
		if ((src == null) || (src.length == 0)) {
			return null;
		}
		return src[randomInt(src.length)];
	}


	public static String randomString(List<String> src) {
		if ((src == null) || src.isEmpty()) {
			return null;
		}
		return src.get(randomInt(src.size()));
	}


	public static String randomString(List<String> src, int maxLength) {
		String result = src.get(randomInt(src.size()));
		while ((maxLength > 0) && (result.length() > maxLength)) {
			result = src.get(randomInt(src.size()));
		}
		return result;
	}


	public static Weighted randomWeighted(Weighted[] src) {
		Weighted result = null;
		if ((src == null) || (src.length == 0)) {
			result = null;
		}
		else {

			double totalWeight = 0.0;
			for (Weighted obj : src) {
				totalWeight += obj.getWeight();
			}

			double selector = randomDouble(totalWeight);
			double offset = 0.0;
			Iterator<Weighted> iter = Arrays.asList(src).iterator();
			while ((result == null) && iter.hasNext()) {
				Weighted obj = iter.next();
				offset += obj.getWeight();
				if (offset > selector) {
					result = obj;
				}
			}
		}

		return result;
	}


	public static Weighted randomWeighted(List<Weighted> src) {
		Weighted result = null;
		if ((src == null) || src.isEmpty()) {
			result = null;
		}
		else {

			double totalWeight = 0.0;
			for (Weighted obj : src) {
				totalWeight += obj.getWeight();
			}

			double selector = randomDouble(totalWeight);
			double offset = 0.0;
			Iterator<Weighted> iter = src.iterator();
			while ((result == null) && iter.hasNext()) {
				Weighted obj = iter.next();
				offset += obj.getWeight();
				if (offset > selector) {
					result = obj;
				}
			}
		}

		return result;
	}


	public static Object randomObject(Object... src) {
		if ((src == null) || (src.length == 0)) {
			return null;
		}
		return src[randomInt(src.length)];
	}


	public static Object randomObject(List src) {
		if ((src == null) || src.isEmpty()) {
			return null;
		}
		return src.get(randomInt(src.size()));
	}


	public static Object[] randomSubset(Object[] src, int minCount, int maxCount) {
		List<Object> result = randomSubset(Arrays.asList(src), minCount, maxCount);
		return result.toArray(new Object[result.size()]);
	}


	public static List<Object> randomSubset(List<Object> src, int minCount, int maxCount) {
		if ((src == null) || (src.size() == 0)) {
			return null;
		}
		minCount = Math.max(minCount, 0);
		if ((maxCount == -1) || (maxCount > src.size())) {
			maxCount = src.size();
		}
		int len = randomInt(minCount, maxCount);
		List<Object> result = new ArrayList<Object>(src);
		Collections.shuffle(result);
		return result.subList(0, len);
	}


	public static List<String> randomStringSubset(List<String> src, int minCount, int maxCount) {
		if ((src == null) || (src.size() == 0)) {
			return null;
		}
		minCount = Math.max(minCount, 0);
		if ((maxCount == -1) || (maxCount > src.size())) {
			maxCount = src.size();
		}
		int len = randomInt(minCount, maxCount);
		List<String> result = new ArrayList<String>(src);
		Collections.shuffle(result);
		return result.subList(0, len);
	}
}
